package com.risk.ctrl.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.risk.ctrl.exceptions.BusinessException;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.apache.commons.collections.MapUtils;

import java.util.Iterator;
import java.util.Map;
import java.util.Objects;

import static com.risk.ctrl.enums.ErrorCodeEnum.OKHTTP_GET_EXCEPTION;
import static com.risk.ctrl.enums.ErrorCodeEnum.OKHTTP_POST_EXCEPTION;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Slf4j
public final class OkhttpUtils {
    private static final MediaType MEDIA_JSON = MediaType.get("application/json; charset=utf-8");

    public static OkHttpClient getOkHttpClient() {
        return OkHttpClientHolder.INSTANCE;
    }

    private static class OkHttpClientHolder {
        private static final OkHttpClient INSTANCE = SpringContextUtil.getBean(OkHttpClient.class);
    }

    private static <T> String joinArgs(Map<String, Object> paramMap, String url) {
        StringBuffer buffer = new StringBuffer(100);
        buffer.append(url);
        if (MapUtils.isNotEmpty(paramMap)) {
            Iterator<Map.Entry<String, Object>> it = paramMap.entrySet().iterator();
            int count = 0;
            while (it.hasNext()) {
                Map.Entry<String, Object> ele = it.next();
                if (Objects.isNull(ele.getValue())) {
                    continue;
                }
                if (count == 0) {
                    buffer.append("?").append(ele.getKey()).append("=").append(ele.getValue());
                } else {
                    buffer.append("&").append(ele.getKey()).append("=").append(ele.getValue());
                }
                count++;
            }
        }
        return buffer.toString();
    }

    private static okhttp3.Headers headers(Map<String, Object> heardMap) {
        okhttp3.Headers.Builder builder = new okhttp3.Headers.Builder();
        if (MapUtils.isNotEmpty(heardMap)) {
            for (Map.Entry<String, Object> entry : heardMap.entrySet()) {
                builder.add(entry.getKey(), entry.getValue().toString());
            }
            return builder.build();
        }
        return null;
    }

    /**
     * get请求
     *
     * @param url
     * @param heardMap
     * @param params
     * @param typeReference
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T, R> R get(String url, Map<String, Object> heardMap, T params, TypeReference<R> typeReference) {
        if (Objects.nonNull(params)) {
            Map<String, Object> paramMap = JSON.parseObject(JSON.toJSONString(params), new TypeReference<Map<String, Object>>() {
            });
            url = joinArgs(paramMap, url);
        }
        Request request;
        Headers headers = headers(heardMap);
        if (Objects.nonNull(headers)) {
            request = new Request.Builder()
                    .url(url)
                    .headers(headers)
                    .build();
        } else {
            request = new Request.Builder()
                    .url(url)
                    .build();
        }
        try (Response httpResponse = getOkHttpClient().newCall(request).execute()) {
            if (!httpResponse.isSuccessful()) {
                log.error("get请求异常,url={}", url);
                throw new BusinessException(OKHTTP_GET_EXCEPTION);
            }
            ResponseBody body = httpResponse.body();
            if (Objects.isNull(body)) {
                return null;
            }
            String responseBody = body.string();
            return JSON.parseObject(responseBody, typeReference);
        } catch (Exception e) {
            log.error("get请求异常,url={}", url);
            throw new BusinessException(OKHTTP_GET_EXCEPTION);
        }
    }

    /**
     * post请求
     *
     * @param url
     * @param params
     * @param heardMap
     * @param paramsMap
     * @param typeReference
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T, R> R post(String url, T params, Map<String, Object> heardMap, Map<String, Object> paramsMap, TypeReference<R> typeReference) {
        url = joinArgs(paramsMap, url);
        RequestBody requestBody = RequestBody.create("", MEDIA_JSON);
        if (Objects.nonNull(params)) {
            requestBody = RequestBody.create(JSON.toJSONString(params), MEDIA_JSON);
        }
        Request request;
        Headers headers = headers(heardMap);
        if (Objects.nonNull(headers)) {
            request = new Request.Builder()
                    .url(url)
                    .post(requestBody)
                    .headers(headers)
                    .build();
        } else {
            request = new Request.Builder()
                    .url(url)
                    .post(requestBody)
                    .build();
        }
        try (Response httpResponse = getOkHttpClient().newCall(request).execute()) {
            if (!httpResponse.isSuccessful()) {
                log.error("post请求异常,url={}", url);
                throw new BusinessException(OKHTTP_POST_EXCEPTION);
            }
            ResponseBody body = httpResponse.body();
            if (Objects.isNull(body)) {
                return null;
            }
            String responseBody = body.string();
            return JSON.parseObject(responseBody, typeReference);
        } catch (Exception e) {
            log.error("post请求异常,url={}", url);
            throw new BusinessException(OKHTTP_POST_EXCEPTION);
        }
    }
}
